#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string>
#include <unistd.h>
#include <cstring>
using namespace std;

#define SIZE 4096
#define PROJID 0x66
#define MODE 0666
#define CREATER "creater"
#define USER "user"
#define PATH "./"
#define ERR_EXIT(m)     \
do                      \
{                       \
    perror(m);          \
    exit(EXIT_FAILURE); \
} while (0);

class Shm
{
private:
    void ShmHeaper(int shmflg)
    {
        _shmid = shmget(_key, _size, shmflg);
        if(_shmid < 0)
            ERR_EXIT("shmget fail!");
    }

    void Create()
    {
        ShmHeaper(IPC_CREAT | IPC_EXCL | _mode);
    }

    void Get()
    {
        ShmHeaper(0);
    }

    void Connect()
    {
        _start_vadder = shmat(_shmid, nullptr, 0); /* nullptr表示让内核去寻找和连接的共享内存, 0表示读写权限全开 */
        if(_start_vadder == (void*) -1)
            ERR_EXIT("shmat fail!");
    }

    void Disconnect()
    {
        int n = shmdt(_start_vadder);
        if(n < 0)
            ERR_EXIT("shmdt fail!");
    }

    void Destroy()
    {
        int n = shmctl(_shmid, IPC_RMID, nullptr);
        if(n < 0)
            ERR_EXIT("shmctl fail!");
    }

public:
    Shm(const char* pathname, const char* usertype, int projid = PROJID, int size = SIZE, mode_t mode = MODE)
        :_pathname(pathname)
        ,_usertype(usertype)
        ,_size(size)
        ,_projid(projid)
        ,_mode(mode)
        ,_shmid(-1)
        ,_start_vadder(nullptr)
    {
        _key = ftok(_pathname.c_str(), _projid);
        if(_key < 0)
            ERR_EXIT("ftok fail!");
        if(_usertype == CREATER)
            Create();
        else if(_usertype == USER)
            Get();
        else
        {
            ERR_EXIT("usertype fail!");
        }
        Connect();
    }

    /* 禁用拷贝构造和赋值重载 */
    Shm(const Shm&) = delete;
    Shm& operator=(const Shm&) = delete;

    /* 获取共享内存虚拟地址以供使用 */
    void* GetShmadder()
    {
        return _start_vadder;
    }

    ~Shm()
    {
        /* creater */
        if(_usertype == CREATER)
            Destroy();
        /* creater / user */
        Disconnect();
    }

private:
    std::string _pathname; /* 路径名称->用于创建_key */
    std::string _usertype; /* 用户种类: 1.->创建者(创建回收共享内存) 2.->使用者(使用共享内存) */
    size_t _size; /* 共享内存的大小 */
    int _projid; /* 创建_key使用的数字，用于增加随机性 */
    mode_t _mode; /* 权限位->用于shmget时创建传入的权限 */
    int _shmid; /* 共享内存的标识符 */
    key_t _key;   /* _key-> 用于创建一个shmget, 使两个进程找到同一共享内存的标识 */
    void* _start_vadder; /* 虚拟地址->连接共享内存返回的虚拟地址，指向共享内存的一个段 */
};